﻿using UnityEngine;
using UnityEditor;
using System.Collections;
using System.Collections.Generic;
using Newtonsoft.Json;

public class Il2cppPatchTool
{
    static void BuildPatchCmd()
    {
		string[] args = System.Environment.GetCommandLineArgs();
		string androidProjectPath = null;
		string outputPath = null;
		int minAppVersion = 0;
		for (int i = 0; i < args.Length; i++)
		{
			if(args[i] == "-project_path")
			{
				androidProjectPath = args[i + 1];
			}
			else if(args[i] == "-output_path")
			{
				outputPath = args[i + 1];
			}
			else if(args[i] == "-min_app_version")
			{
				minAppVersion = int.Parse(args[i + 1]);
			}
			
		}
		
		
		string assetBinDataPath = androidProjectPath + "/src/main/assets/bin/Data/";
        string patchTopPath = outputPath + "/patch";
        string assertBinDataPatchPath = patchTopPath + "/assets_bin_Data/";
     
        if (System.IO.Directory.Exists(patchTopPath)) { CommonUtil.Delete(patchTopPath); }
        System.IO.Directory.CreateDirectory(assertBinDataPatchPath);

        string[][] soPatchFile =
        {
                new string[3]{ "/"+ "jniLibs" + "/armeabi-v7a/libil2cpp.so", "libil2cpp.so.new", "lib_armeabi-v7a_libil2cpp.so.zip" },            
                new string[3]{ "/"+ "jniLibs" + "/arm64-v8a/libil2cpp.so", "libil2cpp.so.new", "lib_arm64-v8a_libil2cpp.so.zip" },
				new string[3]{ "/"+ "jniLibs" + "/x86/libil2cpp.so", "libil2cpp.so.new", "lib_x86_libil2cpp.so.zip" },
        };

        // string[][] soPatchFile =
        // {
                // new string[3]{ "/"+ "jniLibs" + "/armeabi-v7a/libunity.so", "libunity.so.new", "lib_armeabi-v7a_libunity.so.zip" },  
        // };
		
        for (int i = 0; i < soPatchFile.Length; i++)
        {
            string[] specialPaths = soPatchFile[i];
            string projectRelativePath = specialPaths[0];
            string pathInZipFile = specialPaths[1];
            string zipFileName = specialPaths[2];

            string projectFullPath = androidProjectPath + "/src/main" + projectRelativePath;
            ZipHelper.ZipFile(projectFullPath, pathInZipFile, patchTopPath + "/" + zipFileName, 9);
        }

		
        string[] allAssetsBinDataFiles = System.IO.Directory.GetFiles(assetBinDataPath, "*", System.IO.SearchOption.AllDirectories);
        //StringBuilder allZipCmds = new StringBuilder();
        //allZipCmds.AppendFormat("if not exist \"{0}\" (MD \"{0}\") \n", PROJECT_DIR + "/AllAndroidPatchFiles/");
        //allZipCmds.AppendFormat("if not exist \"{0}\" (MD \"{0}\") \n", PROJECT_DIR + "/AllAndroidPatchFiles/assets_bin_Data/");
		
        foreach (string apk_file in allAssetsBinDataFiles)
        {
            string relativePathHeader = "assets/bin/Data/";
            int relativePathStart = apk_file.IndexOf(relativePathHeader);
            string filenameInZip = apk_file.Substring(relativePathStart);                                                //file: assets/bin/Data/xxx/xxx
            string relativePath = apk_file.Substring(relativePathStart + relativePathHeader.Length).Replace('\\', '/'); //file: xxx/xxx
            string zipFileName = relativePath.Replace("/", "__").Replace("\\", "__") + ".bin";                                     //file: xxx__xxx.bin

            //allZipCmds.AppendFormat("cd {0} && {1} -8 \"{2}\" \"{3}\"\n", BUILD_SCRIPTS_PATH, ZIP_PATH, PROJECT_DIR + "/AllAndroidPatchFiles/assets_bin_Data/" + zipFileName, filenameInZip);
			
			string cmdOutput = null;
			string cmdError = null;
			CommandLineTool.CmdExWithOutput("zip", " -8 \"" + patchTopPath + "/assets_bin_Data/" + zipFileName + "\" \"" + filenameInZip + "\"", androidProjectPath + "/src/main", false, true, ref cmdOutput, true, ref cmdError);
			Debug.LogError(cmdOutput);
			Debug.LogError(cmdError);
			if(!string.IsNullOrEmpty(cmdError))
			{
				throw new System.Exception(cmdError);
			}
        }
		
		
		if(true)
		{
			string cmdOutput = null;
			string cmdError = null;
			Debug.LogError("param: " + " -9 -r \"" + outputPath + "/patch.zip\" \"" + patchTopPath + "\"");
			CommandLineTool.CmdExWithOutput("zip", " -9 -r \"" + outputPath + "/il2cpp_patch.zip\" \"" + "." + "\"", patchTopPath, false, true, ref cmdOutput, true, ref cmdError);
			
			Debug.LogError(cmdOutput);
			Debug.LogError(cmdError);
			if(!string.IsNullOrEmpty(cmdError))
			{
				throw new System.Exception(cmdError);
			}
			
			string md5 = MD5Hash.GetMD5HashFromFile(outputPath + "/il2cpp_patch.zip");
			CommonUtil.Move(outputPath + "/il2cpp_patch.zip", outputPath + "/il2cpp_" + md5 + "_" + VersionEx.version_number + "_" + (minAppVersion == 0? VersionEx.version_number:minAppVersion)   + ".zip");
		}
		else
		{
			
		}
		
        // allZipCmds.Append("sleep 1\n");
        // allZipCmds.AppendFormat("cd {0} && {1} -9 -r \"{2}\" \"{3}\"\n", patchTopPath, ZIP_PATH, PROJECT_DIR + "/AllAndroidPatchFiles_Versionx.zip", "*");

        // if (allZipCmds.Length > 0)
        // {
            // string zipPatchesFile = ANDROID_EXPORT_PATH + "/" + "zip_patches.bat";
            // File.WriteAllText(zipPatchesFile, allZipCmds.ToString());
            // File.WriteAllText(zipPatchesFile, allZipCmds.ToString());
            // if (!Exec(zipPatchesFile, zipPatchesFile))
            // {
                // Debug.LogError("exec failed:" + zipPatchesFile);
            // }
        // }
    }

    [MenuItem("Custom/Il2cppPatchTool/TestGetAllCSharpCodeMd5")]
    static void TestGetAllCSharpCodeMd5()
    {
        BuildTarget target = BuildTarget.Android;
        Dictionary<string, string> allCodeMap = GetAllCSharpCodeMd5(target);
        CommonUtil.ObjectToJsonFile(allCodeMap, "all_code_map.txt");
    }

    static Dictionary<string, string> GetAllCSharpCodeMd5(BuildTarget target)
    {
        Dictionary<string, string> allCodeMap = new Dictionary<string, string>();

        var map = GetAllDllMd5(target, false);

        foreach(var entry in map)
        {
            allCodeMap.Add(entry.Value, entry.Key);
        }

        var map2 = GetAllCSharpCodeMd5(false);
        foreach(var entry in map2)
        {
            allCodeMap.Add(entry.Value, entry.Key);
        }

        return allCodeMap;
    }

    static Dictionary<string, string> GetAllDllMd5(BuildTarget target, bool includeNative)
    {
        Dictionary<string, string> ret = new Dictionary<string, string>();
        string[] filePaths = System.IO.Directory.GetFiles("Assets/", "*.dll", System.IO.SearchOption.AllDirectories);
        foreach(var p in filePaths)
        {
            var importer = AssetImporter.GetAtPath(p) as PluginImporter;
            if(importer.isNativePlugin)
            {
                continue;
            }

            if(importer.GetCompatibleWithPlatform(target) || importer.GetCompatibleWithAnyPlatform())
            {
                ret.Add(p, MD5Hash.GetMD5HashFromFile(p));
            }
        }

        return ret;
    }

    static Dictionary<string, string> GetAllCSharpCodeMd5(bool includeEditor)
    {
        Dictionary<string, string> ret = new Dictionary<string, string>();
        string[] filePaths = System.IO.Directory.GetFiles("Assets/", "*.cs", System.IO.SearchOption.AllDirectories);
        foreach(var p in filePaths)
        {
            if(!includeEditor && p.Contains("/Editor/"))
            {
                continue;
            }

            string guid = AssetDatabase.AssetPathToGUID(p);
            string str = CommonUtil.fileToString(p) + " | GDID:" + guid;
            ret.Add(p, MD5Hash.GetMD5HashFromString(str));
        }

        return ret;
    }

    public static void CreateCSharpCodeMd5MapFileByCmd()
    {
        string outputPath = null;
        string compareResultPath = null;
        string[] args = System.Environment.GetCommandLineArgs();
        for (int i = 0; i < args.Length; i++)
        {
            string arg = args[i];
            if (arg == "-output_path")
            {
                outputPath = args[++i];
            }
            else if (arg == "-compare_result_path")
            {
                compareResultPath = args[++i];
            }
        }

        Dictionary<string, string> oldCodeMd5Map = CommonUtil.ObjectFromJsonFile<Dictionary<string, string>>(outputPath);
        if(oldCodeMd5Map != null)
        {
            string dirPath = System.IO.Path.GetDirectoryName(outputPath);
            if(System.IO.Directory.Exists(dirPath))
            {
                System.IO.Directory.CreateDirectory(dirPath);
            }
        }

        CommonUtil.Delete(compareResultPath);
        
        string dirPath2 = System.IO.Path.GetDirectoryName(compareResultPath);
        if(System.IO.Directory.Exists(dirPath2))
        {
            System.IO.Directory.CreateDirectory(dirPath2);
        }

        Dictionary<string, string> newCodeMd5Map = GetAllCSharpCodeMd5(EditorUserBuildSettings.activeBuildTarget);
        
        bool same = false;
        if(oldCodeMd5Map != null)
        {
            same = newCodeMd5Map.Count == oldCodeMd5Map.Count;    
            if(same)
            {
                foreach(var entry in newCodeMd5Map)
                {
                    if(!oldCodeMd5Map.ContainsKey(entry.Key))
                    {
                        same = false;
                        break;
                    }
                }
            }
        }

        if(!same)
        {
            string newStr = Newtonsoft.Json.JsonConvert.SerializeObject(newCodeMd5Map, Formatting.Indented);
            CommonUtil.stringToFile(outputPath, newStr);   
        }

        CommonUtil.stringToFile(compareResultPath, (same).ToString());
    }
}


